package com.cloudfast.utils.word;

import org.apache.poi.xwpf.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author: SONGTIANk
 * @Description: Word替换字符和插入图片工具类
 * @Date: 2020/11/11 17:16
 * @Version: 1.0
 */
public class WordTextUtil {
    private static final Logger logger = LoggerFactory.getLogger(Logger.class);

    /**
     * 根据模板生成word
     *
     * @param path     模板的路径
     * @param params   需要替换的参数
     * @param fileName 生成word文件的文件名
     */
    public void getWord(String path, Map<String, Object> params, String fileName) {
        File file = new File(path);
        // 创建输入流
        try (InputStream is = new FileInputStream(file);) {
            // 创建doc对象
            CustomXWPFDocument doc = new CustomXWPFDocument(is);
            // 替换文本里面的变量
            this.replaceInPara(doc, params);
            // 输出流写入文件
            FileOutputStream out = new FileOutputStream(fileName);
            doc.write(out);
            // 关闭输入输出流
            this.close(is);
            out.close();
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 替换段落里面的变量
     *
     * @param doc    要替换的文档
     * @param params 参数
     */
    private void replaceInPara(CustomXWPFDocument doc, Map<String, Object> params) {
        // 替换table中的内容
        List<XWPFTable> tables = doc.getTables();
        for (int i = 0; i < tables.size(); i++) {
            XWPFTable xwpfTable = tables.get(i);
            List<XWPFTableRow> rows = xwpfTable.getRows();
            for (int row_index = 0; row_index < rows.size(); row_index++) {
                XWPFTableRow row = rows.get(row_index);
                List<XWPFTableCell> cells = row.getTableCells();
                for (int col_index = 0; col_index < cells.size(); col_index++) {
                    XWPFTableCell cell = cells.get(col_index);
                    List<XWPFParagraph> paragraphs = cell.getParagraphs();
                    for (XWPFParagraph _para : paragraphs) {
                        replaceInPara(_para, params, doc);
                    }
                }
            }
        }
        // 普通文本替换
        Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
        XWPFParagraph para;
        while (iterator.hasNext()) {
            para = iterator.next();
            this.replaceInPara(para, params, doc);
        }
    }

    /**
     * 判断${出现的次数
     *
     * @param content
     * @param s
     * @return
     */
    public static int countString(String content, String s) {
        int count = 0;
        while (content.indexOf(s) != -1) {
            content = content.substring(content.indexOf(s) + 1, content.length());
            count++;
        }
        return count;
    }

    private synchronized void replaceInPara(XWPFParagraph para, Map<String, Object> params, CustomXWPFDocument doc) {
        // 获取
        String wordRowText = para.getParagraphText();
        if (this.matcher(wordRowText).find()) {

            int count = countString(wordRowText, "${");
            for (int i = 0; i < count; i++) {
                this.initChangeParams(para, params, doc);
            }
        }
    }

    /**
     * 替换内容
     *
     * @param para
     * @param params
     * @param doc
     */
    private synchronized void initChangeParams(XWPFParagraph para, Map<String, Object> params, CustomXWPFDocument doc) {
        List<XWPFRun> runs = para.getRuns();
        int start = -1;
        int end = -1;
        String str = "";
        for (int i = 0; i < runs.size(); i++) {
            XWPFRun run = runs.get(i);
            String runText = run.toString();
            if ('$' == runText.charAt(0) && '{' == runText.charAt(1)) {
                start = i;
            }
            if ((start != -1)) {
                str += runText;
            }
            if ('}' == runText.charAt(runText.length() - 1)) {
                if (start != -1) {
                    end = i;
                    break;
                }
            }
        }
        for (int i = start; i <= end; i++) {
            para.removeRun(i);
            i--;
            end--;
        }

        // 遍历map
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            this.changeParams(str, key, value, doc, para, start);
        }
    }

    /**
     * 替换参数
     *
     * @param str
     * @param key
     * @param value
     * @param doc
     * @param para
     * @param start
     */
    private synchronized void changeParams(String str, String key, Object value, CustomXWPFDocument doc,
                                           XWPFParagraph para, int start) {
        String patterKey = "${" + key + "}";
        if (str.contains(patterKey)) {
            XWPFRun run = para.insertNewRun(start);
            if (value instanceof WordImage) {
                str = str.replace(patterKey, "");
                WordImage pic = (WordImage) value;
                int width = pic.getWidth();
                int height = pic.getHeight();
                int picType = getPictureType(pic.getType());
                byte[] byteArray = pic.getImageByte();
                ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);
                try {
                    String addPictureData = doc.addPictureData(byteInputStream, picType);
                    doc.createPicture(doc.getAllPictures().size() - 1, addPictureData, width, height, para);
                    para.getRuns().get(start).setText(str, 0);
                } catch (Exception e) {
                    logger.error(e.getMessage());
                }
            } else {
                str = str.replace(patterKey, value + "");
            }
            run.setText(str.replace(patterKey, ""), 0);
        }
    }

    /**
     * 正则匹配字符串
     *
     * @param str
     * @return
     */
    private Matcher matcher(String str) {
        Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(str);
        return matcher;
    }

    /**
     * 根据图片类型，取得对应的图片类型代码
     *
     * @param picType
     * @return int
     */
    private static int getPictureType(String picType) {
        int res = CustomXWPFDocument.PICTURE_TYPE_PICT;
        if (picType != null) {
            if (picType.equalsIgnoreCase("png")) {
                res = CustomXWPFDocument.PICTURE_TYPE_PNG;
            } else if (picType.equalsIgnoreCase("dib")) {
                res = CustomXWPFDocument.PICTURE_TYPE_DIB;
            } else if (picType.equalsIgnoreCase("emf")) {
                res = CustomXWPFDocument.PICTURE_TYPE_EMF;
            } else if (picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")) {
                res = CustomXWPFDocument.PICTURE_TYPE_JPEG;
            } else if (picType.equalsIgnoreCase("wmf")) {
                res = CustomXWPFDocument.PICTURE_TYPE_WMF;
            }
        }
        return res;
    }

    /**
     * 将输入流中的数据写入字节数组
     *
     * @param in
     * @return
     */
    public static byte[] inputStream2ByteArray(InputStream in, boolean isClose) {
        byte[] byteArray = null;
        boolean readState = false;
        try {
            int total = in.available();
            byteArray = new byte[total];
            readState = in.read(byteArray) != -1;
            readState = isClose;
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage());
        } finally {
            if (readState) {
                try {
                    in.close();
                } catch (Exception e2) {
                    e2.getStackTrace();
                }
            }
        }
        return byteArray;
    }

    /**
     * 关闭输入流
     *
     * @param is
     */
    private void close(InputStream is) {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                throw new RuntimeException(e.getMessage());
            }
        }
    }
}
